/*
 * 五星电器公司拥有完全的版权
 * 使用者必须经过许可
 *----------------------------------------------------------------------*
 * Copyright  (c) 2016 FiveStar Co.,Ltd. All rights reserved
 * Author       : FiveStar Development
 * Description  : RequestUrlService.java
 *----------------------------------------------------------------------*
 * Change-History: Change history
 * Developer  Date      Description
 * shidong  2017年6月29日 Short description containing Message, Note ID or CR ID
 *----------------------------------------------------------------------*
 */
package com.twp.auth.service;

import com.twp.auth.annotation.EnableAuth;
import com.twp.auth.annotation.MethodInfo;
import com.twp.auth.config.auth.RequestMappingHandlerConfig;
import com.twp.auth.constants.CacheConstants;
import com.twp.auth.domain.authority.Menu;
import com.twp.auth.domain.authority.RequestUrl;
import com.twp.auth.domain.authority.Role;
import com.twp.auth.myinterface.AuthInfo;
import com.twp.auth.repository.auth.MenuRepository;
import com.twp.auth.repository.auth.RequestUrlRepository;
import com.twp.auth.repository.auth.RoleRepository;
import com.twp.auth.tuple.RequestUrlActionBody;
import com.twp.auth.tuple.RequestUrlDetail;
import com.twp.auth.tuple.RequestUrlRole;
import com.twp.auth.utils.Cache;
import lombok.val;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


/**
 * 此类功能描述
 *
 * @since JDK 1.8
 */
@Transactional
@Service
public class RequestUrlService {

    @Autowired
    AuthInfo authInfo;

    @Autowired
    private RequestUrlRepository requestUrlRepository;

    @Autowired
    private MenuRepository menuRepository;

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    RoleRepository roleRepository;

    @Autowired
    Cache cache;

    @Autowired
    private RequestMappingHandlerConfig requestMappingHandlerConfig;

    public Page<RequestUrl> find(String flag,String menuCode,RequestUrl requestUrl,Pageable pageable){
        if (flag.equals("bind")){
            return requestUrlRepository.findForNeedBindRequest(menuCode,pageable);
        }
        return requestUrlRepository.findAll(
                (root, criteriaQuery, criteriaBuilder) -> {
                    List<Predicate> predicates = new ArrayList<>();

                    if (!StringUtils.isEmpty(menuCode)){
                        /*
                        * 根据flag筛选是否已经绑定的requestMapping
                        * */
//                        if (flag.equals("select")){
//                            predicates.add(criteriaBuilder.equal(
//                                    root.join("menuList", JoinType.INNER).get("code"), menuCode));
//                        }else if(flag.equals("bind")){
//                            predicates.add(criteriaBuilder.notEqual(
//                                    root.join("menuList", JoinType.INNER).get("code"), menuCode));
//                        }
                        predicates.add(criteriaBuilder.equal(
                                    root.join("menuList", JoinType.INNER).get("code"), menuCode));
                    }

                    if(!StringUtils.isEmpty(requestUrl.getId())){
                        predicates.add(criteriaBuilder.equal(root.get("id"),requestUrl.getId()));
                    }
                    if(!StringUtils.isEmpty(requestUrl.getCode())){
                        predicates.add(criteriaBuilder.like(root.get("code"), "%"+requestUrl.getCode()+"%"));
                    }
                    if(!StringUtils.isEmpty(requestUrl.getUrl())){
                        predicates.add(criteriaBuilder.like(root.get("url"), "%"+requestUrl.getUrl()+"%"));
                    }
                    return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
                },
                pageable
        );
    }

    /**
     * 自动更新请求url
     *
     * @since JDK 1.8
     */
    public void updateRequestUrl() {

        /*
        以往版本是直接删掉，但是会影响自增主键不断增加，需要优化
         */
        //requestUrlRepository.deleteAllInBatch();
        EnableAuth enableAuth = authInfo.getClass().getAnnotation(EnableAuth.class);
        String[] restBasePackages = enableAuth.restBasePackages();

        final RequestMappingHandlerMapping requestMappingHandlerMapping = requestMappingHandlerConfig
                .requestMappingHandlerMapping();
        Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();

        Set<RequestMappingInfo> mappings = map.keySet();
        for (RequestMappingInfo info : mappings) {
            HandlerMethod handlerMethod = map.get(info);
            String packageName = handlerMethod.getBeanType().getPackage().getName();

            //^([a-zA-Z]+[.][a-zA-Z]+)[.]*.*
            //
            //匹配
            if (Arrays.stream(restBasePackages).allMatch(s -> Pattern.matches(s, packageName))) {
                String url = info.getPatternsCondition().getPatterns().iterator().next();
                MethodInfo methodInfo = handlerMethod.getMethodAnnotation(MethodInfo.class);
                RequestMethod methods[] = info.getMethodsCondition().getMethods().toArray(new RequestMethod[0]);
                for (int i = 0; i < methods.length; i++) {
                    if (methodInfo != null) {
                        String code = methodInfo.code();
                        String description = methodInfo.description();
                        if (i > 0) {
                            code = code + "_" + i;
                        }
                        RequestMethod m = methods[i];

                        RequestUrl requestUrl = requestUrlRepository.findAllByCode(code);
                        //RequestUrl requestUrl = requestUrlRepository.findByUrlAndMethod(url, m.toString());
                        if (requestUrl == null) {
                            //新增
                            requestUrl = new RequestUrl();
                            requestUrl.setUrl(url);
                        }
                        requestUrl.setClassName(handlerMethod.toString());
                        requestUrl.setCode(code);
                        requestUrl.setName(description);
                        requestUrl.setUpdateTime(new Date());
                        requestUrl.setMethod(m.toString());
                        entityManager.merge(requestUrl);//不存在则增加，存在则更新
                    }
                }
            }
        }
    }

    public List<RequestUrlDetail> selectAllRequestUrlRole() {
//        List<RequestUrlDetail> retlist = (List<RequestUrlDetail>) Cache
//                .get(XGMConstants.CacheConstants.CACHE_URL_ROLE_KEY);

        List<RequestUrlDetail> retlist = cache.getByHazelcast(CacheConstants.CACHE_URL_ROLE_KEY);

        if (retlist != null) {
            return retlist;
        } else {
            Map<String, List<Role>> map = new HashMap<String, List<Role>>();
            List<RequestUrl> urllist = requestUrlRepository.findAll();

            for (RequestUrl url : urllist) {
                if (!map.containsKey(url.getUrl() + url.getMethod())) {
                    map.put(url.getUrl() + url.getMethod(), new ArrayList<Role>());
                }
            }
            Query query = entityManager.createNativeQuery("select distinct a.url,a.method,d.id,d.roleName from twp_request_url a,twp_menu_url b,twp_role_menu c,twp_role d where a.code=b.urlCode and b.menuId=c.menuId and c.roleId=d.id");
//            String url, String method, Integer id, String name

            List<RequestUrlRole> urlrolelist = null;
            List<Object[]> urlroleObjectlist = query.getResultList();
            if (urlroleObjectlist != null && !urlroleObjectlist.isEmpty()) {
                urlrolelist = urlroleObjectlist.stream().map(d -> new RequestUrlRole(
                        String.valueOf(d[0]),//url
                        String.valueOf(d[1]),//method
                        Integer.parseInt(d[2] == null ? null : String.valueOf(d[2])),//id
                        String.valueOf(d[3])//name
                )).collect(Collectors.toList());
            }
            if(urlrolelist != null){
                for (RequestUrlRole urlrole : urlrolelist) {
                    if (map.containsKey(urlrole.getUrl() + urlrole.getMethod())) {
                        List<Role> rolelist = map.get(urlrole.getUrl() + urlrole.getMethod());
                        Role r = new Role();
                        r.setId(urlrole.getId());
                        r.setRoleName(urlrole.getName());
                        rolelist.add(r);
                        map.put(urlrole.getUrl() + urlrole.getMethod(), rolelist);
                    }
                }
            }


            List<RequestUrlDetail> list = new ArrayList<RequestUrlDetail>();
            for (RequestUrl url : urllist) {
                RequestUrlDetail detail = new RequestUrlDetail();
                BeanUtils.copyProperties(url, detail);
                if (map.containsKey(url.getUrl() + url.getMethod())) {
                    detail.setRoleList(map.get(url.getUrl() + url.getMethod()));
                }
                list.add(detail);
            }

            //分组优化
            val group = list.parallelStream().collect(Collectors.groupingBy(d->d.getUrl().contains("{")));

            List<RequestUrlDetail> data_ = new ArrayList<>();
            val groupFalse = Optional.ofNullable(group.computeIfPresent(false,(k,v)->v));
            val groupTrue = Optional.ofNullable(group.computeIfPresent(true,(k,v)->v));

            data_.addAll(groupFalse.orElse(Collections.emptyList()));
            data_.addAll(groupTrue.orElse(Collections.emptyList()));

//            List<RequestUrlDetail> containsPathVariableUrls = list.stream().filter(d -> d.getUrl().contains("{")).collect(Collectors.toList());
//            List<RequestUrlDetail> notContainsPathVariableUrls = list.stream().filter(d -> !(d.getUrl().contains("{"))).collect(Collectors.toList());
//            List<RequestUrlDetail> allRequestUrl = new ArrayList<>(100);
//            allRequestUrl.addAll(notContainsPathVariableUrls);
//            allRequestUrl.addAll(containsPathVariableUrls);
            //Cache.put(XGMConstants.CacheConstants.CACHE_URL_ROLE_KEY, list);
            cache.putByHazelcast(CacheConstants.CACHE_URL_ROLE_KEY, data_);
            return data_;
        }
    }

    public List<RequestUrl> selectAllRequestUrlForRole() {
//        List<RequestUrlDetail> retlist = (List<RequestUrlDetail>) Cache
//                .get(XGMConstants.CacheConstants.CACHE_URL_ROLE_KEY);

        List<RequestUrl> retlist = cache.getByHazelcast(CacheConstants.CACHE_URL_ROLE_KEY);

        if (retlist != null) {
            return retlist;
        } else {
            List<RequestUrl> urlList = requestUrlRepository.findAll();

            for (RequestUrl url:urlList){
                List<Menu> menuList =  url.getMenuList();
                List<Integer> menuIds = menuList.stream().map(menu -> menu.getId()).collect(Collectors.toList());
                if (!menuIds.isEmpty()){
                    url.setRoleList(roleRepository.findAllByMenuIds(menuIds));
                }
//                for (Menu menu:menuList){
//                    url.getRoleList().addAll(menu.getRoleList());
//                }
            }

            //分组优化
            val group = urlList.parallelStream().collect(Collectors.groupingBy(d->d.getUrl().contains("{")));

            List<RequestUrl> data_ = new ArrayList<>();
            val groupFalse = Optional.ofNullable(group.computeIfPresent(false,(k,v)->v));
            val groupTrue = Optional.ofNullable(group.computeIfPresent(true,(k,v)->v));

            data_.addAll(groupFalse.orElse(Collections.emptyList()));
            data_.addAll(groupTrue.orElse(Collections.emptyList()));

//            List<RequestUrlDetail> containsPathVariableUrls = list.stream().filter(d -> d.getUrl().contains("{")).collect(Collectors.toList());
//            List<RequestUrlDetail> notContainsPathVariableUrls = list.stream().filter(d -> !(d.getUrl().contains("{"))).collect(Collectors.toList());
//            List<RequestUrlDetail> allRequestUrl = new ArrayList<>(100);
//            allRequestUrl.addAll(notContainsPathVariableUrls);
//            allRequestUrl.addAll(containsPathVariableUrls);
            //Cache.put(XGMConstants.CacheConstants.CACHE_URL_ROLE_KEY, list);
            cache.putByHazelcast(CacheConstants.CACHE_URL_ROLE_KEY, data_);
            return data_;
        }
    }


    public void action(RequestUrlActionBody actionBody){
        Optional.ofNullable(actionBody.getAction())
                .ifPresent(
                        action->{
                            if (action.equals("addBind")){
                                Menu menu = menuRepository.findAllByCode(actionBody.getMenuCode());
                                if (menu!=null){
                                    List<RequestUrl> requestUrlList = requestUrlRepository.findAllByIdIn(actionBody.getRequestMappingIds());
                                    menu.getRequestUrlList().addAll(requestUrlList);
                                    menuRepository.save(menu);
                                }
                            }else if (action.equals("cancelBind")){
                                requestUrlRepository.cancelBind(actionBody.getMenuCode(),actionBody.getRequestMappingIds());
                            }
                        }
                );
    }
}
